package com.beeasy.tools.bean;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.util.StrUtil;
import ma.glasnost.orika.CustomConverter;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.MappingContext;
import ma.glasnost.orika.impl.DefaultMapperFactory;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;

public abstract class AbstractMapper<U> {
    public static ThreadLocal<Map> localParams = new ThreadLocal(){
        @Override
        protected Object initialValue() {
            return new HashMap<>();
        }
    };

    private Class<U> tClass;
    private static MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

    private static Map<Class, Boolean> registered = new ConcurrentHashMap<>();

    public abstract Object to(Object src, String targetKey, U params);

    public AbstractMapper(){
        Type superClass = getClass().getGenericSuperclass();
        Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
        if (type instanceof ParameterizedType) {
            tClass = (Class<U>) ((ParameterizedType) type).getRawType();
        } else {
            tClass = (Class<U>) type;
        }
        registered.computeIfAbsent(tClass, k -> {
            mapperFactory.classMap(Map.class, tClass).mapNulls(false).mapNullsInReverse(false).register();
            return Boolean.TRUE;
        }) ;
    }

    protected <T> T getValue(Object object, String key){
        if(object instanceof Map){
            return (T) ((Map<?, ?>) object).get(key);
        }
        for (Field field : object.getClass().getDeclaredFields()) {
            if(field.getName().equals(key)){
                field.setAccessible(true);
                try {
                    return (T) field.get(object);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    public final U convertParams(String params){
        Map globalParams = localParams.get();
        Map<String, Object> map = new HashMap<>();
        if (StrUtil.isNotBlank(params)) {
            List<String> arr = StrUtil.split(params, ",", true, true);
            for (String s : arr) {
                int i = s.indexOf("=");
                if(i > -1){
                    String key = s.substring(0, i);
                    String value = s.substring(i+1);
                    //是否需要读取外部参数
                    value = StrUtil.replace(value, "\\$\\{(.+?)(:.+?)?\\}", new Func1<Matcher, String>() {
                        @Override
                        public String call(Matcher matcher) throws Exception {
                            String defaultValue = "";
                            if(matcher.group(2) != null){
                                defaultValue = matcher.group(2).substring(1);
                            }
                            Object val = globalParams.get(matcher.group(1));
                            if (val == null) {
                                return defaultValue;
                            }
                            if(StrUtil.isBlankIfStr(val)){
                                return defaultValue;
                            }
                            return String.valueOf(val);
                        }
                    });
                    map.put(key, value);
                }
            }
        }
        if(Map.class.isAssignableFrom(tClass)){
            return (U) map;
        }
        return cn.hutool.core.bean.BeanUtil.mapToBean(map, tClass, true);
    }
}
